﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
using System.Threading;
using Ooganizer.Platform;
using Ooganizer.API;

namespace Ooganizer.Server
{
    using WorkspaceStateMap = System.Collections.Generic.Dictionary<string, OoganizerState.WorkspaceState>;

    [Guid("04EB0640-7CD9-4935-8AFC-512679A61B89")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
    [ComVisible(true)]
    public interface IOoganizerState
    {
        string GetCurrentWorkspace();
        void SetCurrentWorkspace(string workspaceName);
        void SetWorkspaceState(string workspaceName, OoganizerState.WorkspaceState state);
        OoganizerState.WorkspaceState GetWorkspaceState(string workspaceName);
    }

    [ObjectPooling(MinPoolSize = 1, MaxPoolSize = 1, CreationTimeout = 3000)]
    [MustRunInClientContext(false)]
    [JustInTimeActivation(true)]
    [Description("Contains the current state of the Ooganizer application")]
//    [Transaction(TransactionOption.RequiresNew)]
    [Guid("C3BCD3EF-E01E-46c9-946C-4B1C88BF2549")]
    [ProgId("OoganizerServer.OoganizerState")]
    [ComVisible(true)]
    public class OoganizerState : ServicedComponent, IOoganizerState
    {
        public enum WorkspaceState
        {
            Unloaded, // Default
            //Loaded, // Loaded state is redundant
            Shown,
            Hidden
        }

        public OoganizerState()
        {

        }

        ~OoganizerState()
        {

        }
        
        [AutoComplete]
        public string GetCurrentWorkspace()
        {
            return s_currentWorkspace;
        }

        [AutoComplete]
        public void SetCurrentWorkspace(string workspaceName)
        {
            s_currentWorkspace = workspaceName;
        }

        [AutoComplete]
        public void SetWorkspaceState(string workspaceName, WorkspaceState state)
        {
            s_workspaceState[workspaceName] = state;
        }

        [AutoComplete]
        public WorkspaceState GetWorkspaceState(string workspaceName)
        {
            WorkspaceState state;
            // returns default "Unloaded" state if key doesn't exist
            s_workspaceState.TryGetValue(workspaceName, out state);

            return state;
        }

        /// <summary>
        /// Singleton accessor function to obtain a proxy to the OoganizerAPI
        /// The caller must quickly use and dispose the API ... they will block ALL 
        /// clients until the object is disposed.
        /// 
        /// Note: THE CALLER MUST CALL Dispose() on the returned OoganizerAPIProxy 
        /// </summary>
        /// <returns></returns>        
        public OoganizerAPIProxy GetAPIProxy()
        {
            OoganizerAPIProxy apiProxy = null;

            try
            {
                if (apiSem.WaitOne(s_apiMutexWaitTime))
                {
                    apiProxy = new OoganizerAPIProxy(API);
                }
                else
                {
                    // TimedOutWaiting on the semaphore ... 
                    CheckAPISemaphoreForBadState();                    
                }
            }
            catch (AbandonedMutexException)
            {
                // The last client thread to get a proxy crashed before releasing the mutex
                // Perform any necessary cleanup work, if necessary
                CheckAPISemaphoreForBadState();
            }

            return apiProxy;
        }

        /// <summary>
        /// Clean up the semaphore if needed
        /// 
        /// </summary>
        /// <returns></returns>        
        private void CheckAPISemaphoreForBadState()
        {
            // TODO - Log this ... this should be a rare, but possible incident

            try
            {
                // allow some time before forcing the release of the semaphore
                bool bWaitSucceeded = apiSem.WaitOne(s_apiMaxMutexWaitTime);

                // Either we timed out and we need to force a release OR the semaphore was released
                // before the  maximum timeout and we need to release what we just acquired  
                apiSem.Release();

                if (!bWaitSucceeded)
                {
                    // TODO ... Log this scenario, since it could indicate a bug in a client of the OoganizerServer
                }
            }
            catch (WaitHandleCannotBeOpenedException e)
            {
                // Semaphore does not exist ... this should never happen!
                throw e;
            }
        }

        private static OoganizerAPI API
        {
            get
            {
                if (s_ooganizerAPI == null)
                {
                    lock (s_apiLockObj)
                    {
                        if (s_ooganizerAPI == null)
                        {
                            try
                            {
                                s_ooganizerAPI = new OoganizerAPI();
                            }
                            catch (Exception)
                            {
                                // Log something
                            }
                        }
                    }
                }

                return s_ooganizerAPI;
            }
        }

        private static Semaphore apiSem
        {
            get
            {
                if (s_apiSem == null)
                {
                    lock (s_semLockObj)
                    {
                        if (s_apiSem == null)
                        {
                            try
                            {
                                s_apiSem = new Semaphore(1, 1, s_apiSemName);
                            }
                            catch (Exception)
                            {
                                // Log something
                            }
                        }
                    }
                }

                return s_apiSem;
            }
        }
        
        internal static string s_apiSemName = "OOGANIZERAPI_NAMED_SYSTEM_MUTEX";
        private static Object s_apiLockObj = new Object();
        private static Object s_semLockObj = new Object();
        private static volatile OoganizerAPI s_ooganizerAPI;
        private static Semaphore s_apiSem;
        private static int s_apiMutexWaitTime = 500;
        private static int s_apiMaxMutexWaitTime = 30000;

        // Ooganizer state
        private static string s_currentWorkspace;
        private static WorkspaceStateMap s_workspaceState = new WorkspaceStateMap();
    }

    [Serializable]
    public class OoganizerAPIProxy : Disposable
    {
        public OoganizerAPIProxy(OoganizerAPI ooganizerAPI)
        {
            _ooganizerAPI = ooganizerAPI;
        }

        protected override void Dispose(bool bDisposing)
        {
            if (bDisposing)
            {
                // Clean up managed resources                
                try
                {
                    // Attempt to open the named semaphore.
                    Semaphore sem = Semaphore.OpenExisting(OoganizerState.s_apiSemName);

                    // Release the semaphore since we are disposing the proxy
                    sem.Release();
                }
                catch (WaitHandleCannotBeOpenedException e)
                {
                    // Semaphore does not exist ... this should never happen!
                    throw e;
                }
            }

            // Clean up all native resources
        }

        public OoganizerAPI API
        {
            get
            {
                return _ooganizerAPI;
            }
        }

        private OoganizerAPI _ooganizerAPI;
    }
}
